package problemSpace;

import java.util.ArrayList;
import java.util.Date;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;

@XmlRootElement(name = "Vehicle")
public class Vehicle {
	@XmlElement(name = "VehicleType")
	public String vehicleType;
	@XmlElement(name = "VehicleCode")
	public String vehicleCode;
	@XmlElement(name = "PumpLineLength")
	public String pumpLineLength;

	@XmlTransient
	public int getPumpLineLengthM() {
		return Integer.parseInt(pumpLineLength.substring(0,pumpLineLength.length()));
	}

	@XmlElement(name = "NormalVolume")
	public double normalVolume;
	@XmlElement(name = "MaximumVolume")
	public double maximumVolume;
	@XmlElement(name = "DischargeM3PerHour")
	public double dischargeM3PerHour;
	@XmlTransient
	public ArrayList<timeInterval> occupied;
	@XmlTransient
	public String preferedStationCode;
	@XmlTransient
	public Date startUnloading;
	@XmlTransient
	public double volumeToDeliver;
	@XmlTransient
	public int tMax;
	@XmlTransient
	public Date lastUnloadingForOrder;

	public Vehicle() {
		this.occupied = new ArrayList<timeInterval>();
	}
	
	public Vehicle(Vehicle kamion){
		this.vehicleType = kamion.vehicleType;
		this.vehicleCode = kamion.vehicleCode;
		this.pumpLineLength = kamion.pumpLineLength;
		this.normalVolume = kamion.normalVolume;
		this.maximumVolume = kamion.maximumVolume;
		this.dischargeM3PerHour = kamion.dischargeM3PerHour;
		this.occupied = new ArrayList<timeInterval>();
		for( timeInterval ti : kamion.occupied) this.occupied.add(new timeInterval(ti));
	}

	public Vehicle(String vhType, String vehicleCode, String pumpLineLength,
			double normalVolume, double maximumVolume, double throuphutM3H) {
		this.vehicleType = vhType;
		this.vehicleCode = vehicleCode;
		this.pumpLineLength = pumpLineLength;
		this.normalVolume = normalVolume;
		this.maximumVolume = maximumVolume;
		this.dischargeM3PerHour = throuphutM3H;
		this.occupied = new ArrayList<timeInterval>();
	}
	/**
	 * Verwijdert een tijdsinterval uit de lijst met intervallen wanneer het vehicle bezet is.
	 */
	public int removeTimeInterval(String orderCode){
		int numberDeleted = 0;
		for(int i = occupied.size()-1; i>=0; i--){
			if(occupied.get(i).orderCode.equals(orderCode)){
				occupied.remove(i);
				numberDeleted++;
			}
		}
		return numberDeleted;
	}

	/**
	 * Checks the possibility to do a delivery for a given order, if so, makes
	 * the delivery and sets the timeIntervals for the vehicle and the station
	 * @return True if a delivery is scheduled with this vehicle
	 */
	public boolean satisfies(Order order, Station station) {
		// Als de pomplengte niet voldoet => false
		if (this.getPumpLineLengthM() < order.getPumpLineLengthRequiredM()
				|| this.normalVolume > this.maximumVolume
				|| order.remainingVolume <= 0 
				|| this.normalVolume <= 0)
			return false;

		// dit is geen hard constraint, dus doen we dit liever niet, enkel als het niet anders kan
/*		if (this.dischargeM3PerHour < order.requiredDischargeM3PerHour && order.failedTries>=1)
			return false;
*/
		// Als het type wagen niet voldoet => false
		for (String type : order.prohibitedTypes) {
			if (this.vehicleType.compareTo(type) > 0)
				return false;
		}

		// De wagen voldoet, we gaan na of hij vrij is voor dit order
		Station fromStation = null;
		@SuppressWarnings("unused")
		Station toStation = null;

		// Standaard wordt het voorkeursstation aangenomen voor het reistraject.
		fromStation = station;
		toStation = station;

		// We nemen het kortste reistraject
		/*
		 * StationDuration kortste = null; for(StationDuration sd :
		 * order.constructionYard.stDu){ if(kortste == null ||
		 * kortste.drivingMinutes>sd.drivingMinutes){ kortste = sd; } }
		 * for(Station s : stationList){
		 * if(s.stationCode.equals(kortste.stationCode)){ fromStation = s;
		 * toStation = s; break; } }
		 */

		Date startLoadingAtStation = null;
		Date departureAtStation = null;
		Date backAtStation = null;
		Date arrivalAtYard = order.from;
		int drivingMinutes = 0;
		for (StationDuration sd : order.constructionYard.stDu) {
			if (sd.stationCode.equals(fromStation.stationCode)) {
				// TODO als stationDuration "from" en "to" verschillend zouden zijn, hierop controleren
				drivingMinutes = sd.drivingMinutes;
				break;
			}
		}
		// ConstructionYard not found!
		if(drivingMinutes==0){
			System.err.println("Vehicle: satisfies(): constructionYard not found");
			return false;
		}
		departureAtStation = dateSubstract(arrivalAtYard,drivingMinutes * 60000);
		startLoadingAtStation = dateSubstract(departureAtStation,fromStation.loadingMinutes * 60000);

		// check of het laadstation niet bezet is, anders schuiven
		timeInterval stationInterval = new timeInterval(startLoadingAtStation,departureAtStation, order.orderCode);
		int i = 0;
		while (stationInterval.overlaps(station.occupied)) {
			// 15 sec opschuiven (later)
			arrivalAtYard = dateSum(arrivalAtYard, 15000);
			// tijdstippen opnieuw berekenen
			departureAtStation = dateSubstract(arrivalAtYard,drivingMinutes * 60000);
			startLoadingAtStation = dateSubstract(departureAtStation,fromStation.loadingMinutes * 60000);
			stationInterval = new timeInterval(startLoadingAtStation,departureAtStation, order.orderCode);
			i++; // om problemen op te vangen: niet blijven verschuiven
			if (i > 80){// na 20 min opgeschoven ander station proberen
				if(order.failedTries < order.constructionYard.stDu.size()) return false;
				if(i > 480) { // Delivery max 120 min opschuiven (later)
					System.out.println("Delivery redelijk veel opgeschoven!!");
					break;
				}
				
			}
		}

		this.startUnloading = dateSum(arrivalAtYard,order.constructionYard.waitingMinutes * 60000);
		if (order.remainingVolume < this.normalVolume)
			this.volumeToDeliver = order.remainingVolume;
		else
			this.volumeToDeliver = normalVolume;
		double unloadingMinutes = 0;
		unloadingMinutes = this.volumeToDeliver / order.requiredDischargeM3PerHour * 60;

		this.tMax = (int) unloadingMinutes + drivingMinutes + order.constructionYard.waitingMinutes+5;
		// Controle op de tijd van een delivery zodat een vrachtwagen niet te lang levert gegeven de requiredDischargeM3PerHour
		if (this.tMax > 100) {
			double volume = (100 - drivingMinutes - order.constructionYard.waitingMinutes) / 60 * order.requiredDischargeM3PerHour;
			// verhouding < 50%? probeer eerst wat kleinere camions om niet te veel te leveren
			if (volume / normalVolume < 0.50 && order.failedTries < 20)
				return false;
			// *1000)/1000 voor het afronden naar 3 cijfers na de komma
			this.volumeToDeliver = Math.floor(volume * 1000) / 1000;
			unloadingMinutes = this.volumeToDeliver / order.requiredDischargeM3PerHour * 60;
		}

		this.lastUnloadingForOrder = dateSum(this.startUnloading, (long) (unloadingMinutes * 60000));
		// TODO Sammy ik heb 't niet gevonden, hieronder zelf 35 min proberen bijtellen en nog altijd dezelfde HC die overtreden wordt... -- Wouter
		backAtStation = dateSum(this.lastUnloadingForOrder, (drivingMinutes) * 60000);

		// We maken het nieuwe tijdsinterval aan
		timeInterval vehicleInterval = new timeInterval(startLoadingAtStation, backAtStation, order.orderCode);

		// Als dit tijdsinterval niet overlapt met een ander tijdsinterval, mag dit toegevoegd worden
		if (!vehicleInterval.overlaps(occupied)) {
			occupied.add(vehicleInterval);
			station.occupied.add(stationInterval);
			return true;
		} else return false;
	}

	/**
	 * Substract millis from a given date
	 * @param date Date object
	 * @param time Time in millis
	 * @return new Date object with the calculated date
	 */
	private Date dateSubstract(Date date, long time) {
		return new Date(date.getTime() - time);
	}

	/**
	 * Add millis to a given date
	 * @param date Date object
	 * @param time Time in millis
	 * @return new Date object with the calculated date
	 */
	private Date dateSum(Date date, long time) {
		return new Date(date.getTime() + time);
	}
}
